/*
 * Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.liu.generator;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.liu.common.enums.HttpStatus;
import com.liu.common.exception.FileServerException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ExceptionHandler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.Serializable;
import java.util.List;
import java.util.Map;

/**
 * 响应信息主体
 *
 * @param <T>
 * @author lengleng
 */
public class R<T> implements Serializable {

	private static final Logger LOGGER = LoggerFactory.getLogger(R.class);

	private static final long serialVersionUID = 1L;

	private int code;

	private String msg;

	private T data;

	public static <T> R<T> ok() {
		return restResult(null, HttpStatus.OK.value(), "成功");
	}

	public static <T> R<T> ok(T data) {
		return restResult(data, HttpStatus.OK.value(), "成功");
	}
	public static <T> R<T> ok(int code, String msg) {
		return restResult(null, code, msg);
	}

	public static <T> R<T> ok(T data, String msg) {
		return restResult(data, HttpStatus.OK.value(), msg);
	}

	public static <T> R<T> ok(T data,HttpStatus httpStatus) {
		return restResult(data, httpStatus.value(), httpStatus.msg());
	}

	public static <T> R<T> failed() {
		return restResult(null, HttpStatus.INTERNAL_SERVER_ERROR.value(), "失败");
	}

	public static <T> R<T> failed(String msg) {
		return restResult(null, HttpStatus.INTERNAL_SERVER_ERROR.value(), msg);
	}
	public static <T> R<T> failed(int code,String msg) {
		return restResult(null, code, msg);
	}

	public static <T> R<T> failed(T data) {
		return restResult(data, HttpStatus.INTERNAL_SERVER_ERROR.value(), "失败");
	}

	public static <T> R<T> failed(T data, String msg) {
		return restResult(data, HttpStatus.INTERNAL_SERVER_ERROR.value(), msg);
	}
	public static <T> R<T> failed(T data,int code, String msg) {
		return restResult(data, code, msg);
	}
	public static <T> R<T> failed(T data,HttpStatus httpStatus) {
		return restResult(data, httpStatus.value(), httpStatus.msg());
	}

	private static <T> R<T> restResult(T data, int code, String msg) {
		R<T> apiResult = new R<>();
		apiResult.setCode(code);
		apiResult.setData(data);
		apiResult.setMsg(msg);
		return apiResult;
	}


	public static long getSerialVersionUID() {
		return serialVersionUID;
	}

	public int getCode() {
		return code;
	}

	public void setCode(int code) {
		this.code = code;
	}

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}

	public T getData() {
		return data;
	}

	public void setData(T data) {
		this.data = data;
	}

	@Override
	public String toString() {
		return "R{" +
				"code=" + code +
				", msg='" + msg + '\'' +
				", data=" + data +
				'}';
	}


	/** 异常处理 */
	@ExceptionHandler(Exception.class)
	public void exceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception ex)
			throws Exception {
		Map<String,Object> map = Maps.newHashMap();
		int status = 0;
		// 方法级别shiro权限校验失败时异常信息处理
		if(ex instanceof FileServerException) {
			status = HttpStatus.INTERNAL_SERVER_ERROR.value();
			String message = StringUtils.isEmpty(ex.getMessage()) ? ex.getCause().getMessage() : ex.getMessage();
			map.put("msg",message);
			map.put("code", HttpStatus.ZUUL_UPLOAD_FAIL.value());
		} else if(ex instanceof MethodArgumentNotValidException){
			status = HttpStatus.INTERNAL_SERVER_ERROR.value();
			map.put("code", HttpStatus.VALIDATED_FAIL.value());
			MethodArgumentNotValidException mane = (MethodArgumentNotValidException)ex;
			BindingResult bindingResult = mane.getBindingResult();
			List<String> errorMessage = Lists.newArrayList();
			List<FieldError> fes = bindingResult.getFieldErrors();
			for (FieldError fe : fes) {
				errorMessage.add(fe.getDefaultMessage());
			}
			map.put("msg", Joiner.on(",").join(errorMessage));
		} else if(ex instanceof HttpMessageNotReadableException) {
			HttpMessageNotReadableException je = (HttpMessageNotReadableException)ex;
			status = HttpStatus.INTERNAL_SERVER_ERROR.value();
			map.put("code", HttpStatus.INTERNAL_SERVER_ERROR.value());
			if(StringUtils.contains(je.getMessage(),"out of range")) {
				map.put("msg","排序应在1-127之间");
			} else {
				map.put("msg",HttpStatus.INTERNAL_SERVER_ERROR.msg());
			}

		} else if(ex instanceof RuntimeException) {
			status = HttpStatus.INTERNAL_SERVER_ERROR.value();
			map.put("code", HttpStatus.INTERNAL_SERVER_ERROR.value());
			String message = StringUtils.isEmpty(ex.getMessage()) ? ex.getCause().getMessage() : ex.getMessage();
			LOGGER.error(message,ex);
			map.put("msg",message);
		}

		if(status == 0) {
			status = HttpStatus.INTERNAL_SERVER_ERROR.value();
			map.put("code", HttpStatus.INTERNAL_SERVER_ERROR.value());
			map.put("msg", HttpStatus.INTERNAL_SERVER_ERROR.msg());
		}
		LOGGER.error(ex.getMessage(),ex);
		response.setContentType("application/json;charset=UTF-8");
		response.setHeader("Access-Control-Allow-Origin","*");
		response.setStatus(status);
		map.put("timestamp", System.currentTimeMillis());
		response.getOutputStream().write(new ObjectMapper().writeValueAsString(map).getBytes());
	}



}
